package org.imdm.portal.loadchecker.telnet;

import org.apache.commons.net.telnet.*;
import org.apache.commons.net.*;
import java.io.*;
import java.util.*;

/**
 * This class extends Telnet Client, adds some new methods for easy use.
 *
 * @author Guang Zhou
 * @version 1.0
 */
public class TelnetClientAdvance extends TelnetClient{

  private InputStream in;
  private PrintStream out;
  private static String[] prompts = {"loadchecker:"};
  private String server;
  private String user;
  private String password;
  private boolean login = false;

  private ArrayList<ProtocolCommandListener> listeners;

  public TelnetClientAdvance() {
    listeners = new ArrayList<ProtocolCommandListener>();
  }

  public boolean hasLogin(){return (super.isConnected() && login);}
  public void connect() throws TelnetException {

    // Connect to the specified server
    try {
      connect(server, 23);
    }
    catch (Exception ex) {
      System.err.println("Can't telnet to the server " + server);
      System.err.println(ex.getMessage());
//      ex.printStackTrace();
      login = false;
      throw new TelnetException("Can't telnet to the server " + server);
    }

    try {
      // Get input and output stream references
      in = getInputStream();
      out = new PrintStream(getOutputStream());

      // Log the user on
      readUntil("login: ");
      write(user);
      readUntil("Password: ");
      write(password);

      // Advance to a prompt
      // TODO: maybe not $ and #
      String successfulLoginPrompts[] = {"$", "#"};
      readUntil(successfulLoginPrompts);

      // send commands to set a special prompt to be synchronization character
      sendCommandIgnoreResponse("csh");
      String p = sendCommand("set prompt='" + prompts[0] + "'");
      if ( p.indexOf(prompts[0]) >= 0 ) {
        login = true;
      }
      else {
        login = false;
        throw new TelnetException("Failed to login server " + server);
      }
    }
    catch (Exception e) {
      System.err.println("Fail to connect to "+server);
      disconnect();
      login = false;
      if(e instanceof TelnetException){
    	  throw (TelnetException)e;
      }
    }
  }

  public void setServer(String server) {
    this.server = server;
  }

  public void setUser(String user) {
    this.user = user;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  private String readUntil(String[] pattern) throws Exception {
//    char lastChar = ' ';
    StringBuffer sb = new StringBuffer();
    int r = in.read();
    char ch = (char)r;
    while (-1 != r) {
      fireReplyReceived(ch);
      sb.append(ch);

      if (sb.toString().indexOf("Login incorrect")>=0) {
    	  throw new TelnetException("Login incorrect");
      }else if(sb.toString().indexOf("closed by foreign host")>=0){
    	  throw new TelnetException("Closed by foreign host");
      }else if(sb.toString().indexOf("Unknown host")>=0){
    	  throw new TelnetException("Unknown host");
      }else if(sb.toString().indexOf("Connect failed")>=0){
    	  throw new TelnetException("Connect failed");
      }else if(sb.toString().indexOf("Connection to host lost")>=0){
    	  throw new TelnetException("Connection to host lost");
      }else if(sb.toString().indexOf("Choose a new password")>=0){
    	  throw new TelnetException("Choose a new password");
      }else if(sb.toString().indexOf("No route to host")>=0){
	  	  throw new TelnetException("No route to host");
	  }

      for (int i = 0; i < pattern.length; i++) {
        if (sb.toString().endsWith(pattern[i])) {
//System.err.println("\n>>> readUntil() pattern match" + pattern[i]);
//System.err.println(sb.toString());
//System.err.println();
          return sb.toString();
        }
      }

      ch = (char)(r = in.read());
    }
//System.err.println("\n>>> readUntil() -1 == r");
//System.err.println(sb.toString());
//System.err.println();
    return sb.toString();
  }
/*
  private boolean isLastChar(char ch){
    if(ch==' '||ch==':'){
      return true;
    }
     return false;
  }
*/
  private String readUntil(String pattern)throws Exception {
     return readUntil(new String[]{pattern});
  }

  private void write(String value) throws Exception {
    try {
      out.println(value);
      out.flush();
    }
    catch (Exception ex) {
    	System.err.println("Write Error!");
      throw ex;
    }
  }

  public boolean abort(){

    String command = "ABOR";

    try {
      _socket_.sendUrgentData(245);
    }
    catch (IOException ex) {
    	System.err.println("Abort error!");
    	return false;
    }
    fireCommandSent(command);
    login = false;
    return true;
  }

  /**
   * Send command to the remote server and wait for the prompt.
   * @param command String
   * @param prompt String
   * @return String
   */
  public String sendCommand(String command, String prompt)throws TelnetException {
    return sendCommand(command,new String[]{prompt});
  }

  /**
   * Send the command to the remote server and wait for the default prompt.
   * @param command String
   * @return String
   */
  public String sendCommand(String command) throws TelnetException{
    return sendCommand(command,prompts);
  }

  private String sendCommand(String command, String[] ps) throws TelnetException {
    // System.out.println("Send Command: [" + command + "]  Prompts: [" + ps + "]");
	try {
		write(command);
		fireCommandSent(command);
		return readUntil(ps);
    }
    catch (Exception ex) {
      throw new TelnetException("Send command "+command+" error!");
    }
  }
  
  private void sendCommandIgnoreResponse(String command) throws TelnetException {
	try {
	  write(command);
	  fireCommandSent(command);
//System.err.println("=== in.available() = " + in.available());
//	  in.skip(in.available());
	}
	catch (Exception ex) {
	  throw new TelnetException("Send command "+command+" error!");
	}
  }

  public void disconnect() {
    try {
      if (isConnected()) {
        super.disconnect();
        login = false;
      }
    }
    catch (Exception ex) {
    	System.err.println("Telnet disconnect error!");
    }
  }


  public void setTelnetListener(ProtocolCommandListener listener) {
    this.listeners.add(listener);
  }

  private void fireCommandSent(String command) {
    for (int i = 0; listeners != null && i < listeners.size(); i++) {
      ProtocolCommandListener listener = (ProtocolCommandListener) listeners.
          get(i);
      ProtocolCommandEvent event = new ProtocolCommandEvent(this, null, command);
      listener.protocolCommandSent(event);
    }
  }

  private void fireReplyReceived(char message) {
    for (int i = 0; listeners != null && i < listeners.size(); i++) {
      ProtocolCommandListener listener = (ProtocolCommandListener) listeners.
          get(i);
      ProtocolCommandEvent event = new ProtocolCommandEvent(this, null,
          new String(new char[] {message}));
      listener.protocolReplyReceived(event);
    }
  }

	public String getUser() {
		return user;
	}

}
